using System;
using System.Collections.Generic;
using System.Linq;
using NHibernate;
using NHibernate.Criterion;
using NHibernate.Linq;
using FluentNHibernate;
using FluentNHibernate.Mapping;

namespace seanfoy.paging {
    public class Paging {
        public static IList<T> Page<T>(ISession s, DetachedCriteria unpagedData, IEnumerable<String> order, T start, int pageSize) {
            unpagedData.Add(geq(order, start));
            var c = unpagedData.GetExecutableCriteria(s);
            c.SetMaxResults(pageSize);
            return c.List<T>();
        }
        public static ICriterion geq(IEnumerable<String> order, Object c) {
            if (Object.ReferenceEquals(c, null)) return new Conjunction();
            if (Object.ReferenceEquals(order, null)) return new Conjunction();
            // gt(o_1(c)) || (eq(o_1(c)) && gt(o_2(c))) || ...
            //               (eq(o_1(c)) && ... & eq(o_m(c)) && gt(o_n(c))) 
            var cType = c.GetType();
            return
                foldr(
                    Enumerable.Select(
                        order,
                        o => new {
                                 name = o,
                                 value = cType.GetProperty(o).GetValue(c, emptyArgs)}),
                    (ICriterion)new Conjunction(),
                    (a, i) => (
                               new OrExpression(
                                   new SimpleExpression(i.name, i.value, ">"),
                                   new AndExpression(
                                       new SimpleExpression(i.name, i.value, "="),
                                       a))));
        }
        public static U foldr<T, U>(IEnumerable<T> seq, U seed, Func<U, T, U> f) {
            return
                Enumerable.Aggregate(
                    Enumerable.Reverse(seq),
                    seed,
                    f);
        }
        private static Object [] emptyArgs = new Object [] {};

        public static void AddOrder(DetachedCriteria c, IEnumerable<String> order) {
            foreach (var o in order) {
                c.AddOrder(Order.Asc(o));
            }
        }
    }
}
